package exercise4;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BoundedQueue<E> {
	/**
	 * Constructs an empty queue.
	 * 
	 * @param capacity
	 *            the maximum capacity of the queue
	 */
	public BoundedQueue(int capacity) {
		elements = new Object[capacity];
		head = 0;
		tail = 0;
		size = 0;
	}

	/**
	 * Removes the object at the head.
	 * 
	 * @return the object that has been removed from the queue
	 */
	public E remove() throws InterruptedException {
		queueLock.lock();
		try {
			while (size == 0)
				valueAvailableCondition.await();
			E r = (E) elements[head];
			head++;
			size--;
			if (head == elements.length)
				head = 0;
			spaceAvailableCondition.signalAll();
			return r;
		} finally {
			queueLock.unlock();
		}
	}

	/**
	 * Appends an object at the tail.
	 * 
	 * @param newValue
	 *            the object to be appended
	 */
	public void add(E newValue) throws InterruptedException {
		queueLock.lock();
		try {
			while (size == elements.length)
				spaceAvailableCondition.await();
			elements[tail] = newValue;
			tail++;
			size++;
			if (tail == elements.length)
				tail = 0;
			valueAvailableCondition.signalAll();
		} finally {
			queueLock.unlock();
		}
	}

	private Object[] elements;
	private int head;
	private int tail;
	private int size;

	private Lock queueLock = new ReentrantLock();
	private Condition spaceAvailableCondition = queueLock.newCondition();
	private Condition valueAvailableCondition = queueLock.newCondition();
}